/**
 * 
 */
package com.jplus.core.core.abstracts.defaults;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jplus.core.core.abstracts.Environment;
import com.jplus.core.utill.Assert;
import com.jplus.core.utill.ElEngine;
import com.jplus.core.utill.JUtil;
import com.jplus.core.utill.PathUtil;
import com.jplus.core.utill.clazz.ClassSearchUtil;

/**
 * 默认读取 properties
 * 
 * @author yuanqy
 */
public class DefaultEnvironment extends Environment {

	private static DefaultEnvironment env;
	private final Logger log = LoggerFactory.getLogger(DefaultEnvironment.class);
	private final String SP = File.separator;
	private final Map<String, Properties> props = new ConcurrentHashMap<>();

	// classPath下，如果有此文件，自动加载
	private final String[] autoLoad = { "app.properties", "application.properties" };

	private DefaultEnvironment() {
		super();
		for (String str : autoLoad) {
			Properties prop = ClassSearchUtil.getProperties(str);
			if (prop != null) {
				log.info("[Prop]:{}", str);
				for (Entry<Object, Object> entry : prop.entrySet()) {
					log.info("\t>> {} = {}", entry.getKey(), entry.getValue());
				}
				props.put(str, prop);
			}
		}
	}

	public static DefaultEnvironment getSingleton() {
		if (env == null)
			env = new DefaultEnvironment();
		return env;
	}

	@Override
	public Properties loadConfigFile(String... propFile) {
		Set<File> files = buildFile(propFile);
		return loadConfigFile(files.toArray(new File[files.size()]));
	}

	private Properties loadConfigFile(File... files) {
		Properties prop = new Properties();
		InputStream is = null;
		try {
			for (File file : files) {
				is = new FileInputStream(file);
				Properties temp = new Properties();
				temp.load(is);
				prop.putAll(temp);
				props.put(file.getName(), temp);// ----------
				log.info("[Prop]:{}", file.getPath());
				for (Entry<Object, Object> entry : prop.entrySet()) {
					log.info("\t>> {} = {}", entry.getKey(), entry.getValue());
				}
			}
		} catch (FileNotFoundException e) {
			log.error("PropertyFile is not fount", e);
		} catch (IOException e) {
			log.error("Read IO is Exception", e);
		} finally {
			try {
				if (is != null)
					is.close();
			} catch (IOException e) {
				log.error("Close IO is Exception", e);
			}
		}
		return prop;
	}

	/**
	 * 加载配置文件默认规则，当然你也可以自定义规则,举例：<br>
	 * 
	 * <pre>
	     * "file:/app.properties"           //系统根目录
	     * "app.properties"                 //项目根目录
	     * "/app.properties"                //项目WEB-INF下
	     * "classpath:app.properties"       //项目ClassPath下,
	 * </pre>
	 * 
	 * @param propFile 配置规则
	 */
	protected Set<File> buildFile(String... filePath) {
		Set<File> setfile = new HashSet<>();
		Assert.notEmpty(filePath);
		for (String fileStr : filePath) {
			Set<String> propPath = getPath(fileStr);
			for (String path : propPath) {
				String temp = JUtil.formatPath(path);
				File f = new File(temp);
				if (f.exists() && f.isFile())
					setfile.add(f);
				else
					log.error("[File is not exists]:" + path);
			}
		}
		return setfile;
	}

	/**
	 * 加载配置文件默认规则，当然你也可以自定义规则,举例：<br>
	 * 
	 * <pre>
	     * "file:/XXX/"                 //系统根目录下 
	     * "XXX/"                       //项目根目录下 
	     * "/XXX/"                      //项目WEB-INF下
	     * "classpath:XXX/"             //项目ClassPath下,
	 * </pre>
	 * 
	 * @param folderPath 配置规则
	 * @param trans      递归层级，>=1
	 */
	protected Set<File> buildFolder(int trans, String... folderPath) {
		Set<File> setfile = new HashSet<>();
		Assert.notEmpty(folderPath);
		for (String fileStr : folderPath) {
			Set<String> propPath = getPath(fileStr);
			for (String path : propPath) {
				String temp = JUtil.formatPath(path);
				File f = new File(temp);
				if (f.exists()) {
					if (f.isFile())
						setfile.add(f);
					if (f.isDirectory() && trans > 0) {
						String[] chilePath = f.list();
						for (String spath : chilePath) {
							int tt = trans - 1;
							setfile.addAll(buildFolder(tt, "file:" + f.getPath() + SP + spath));
						}
					}
				}
			}
		}
		return setfile;
	}

	/**
	 * path 如果用;分割，则算作多个path路径
	 */
	private Set<String> getPath(String path) {
		Set<String> setPath = new HashSet<>();
		if (JUtil.isEmpty(path))
			return setPath;
		path = JUtil.trimAll(path);
		String[] fs = path.split(";");
		for (int i = 0; i < fs.length; i++) {
			String file = fs[i];
			int index = file.indexOf(":");
			String propPath = "";
			if (index < 0)
				propPath = PathUtil.getWebRootPath() + SP
						+ ((file.charAt(0) == '/') || (file.charAt(0) == '\\') ? "WEB-INF" : "") + file;
			else if (file.substring(0, index).equalsIgnoreCase("classpath"))
				propPath = PathUtil.getRootClassPath() + SP + file.substring(index + 1, file.length());
			else if (file.substring(0, index).equalsIgnoreCase("file")) {
				propPath = SP + file.substring(index + 1, file.length());
			}
			propPath = JUtil.formatPath(propPath);
			setPath.add(propPath);
		}
		return setPath;
	}

	private String getAllProp(String key) {
		String val = null;
		for (Entry<String, Properties> entry : props.entrySet()) {
			val = entry.getValue().getProperty(key);
			if (val != null)
				break;
		}
		if (val == null)
			return getSysEnv(key);
		return val;
	}

	@Override
	public String getProp(String key) {
		if (key.startsWith("${") && key.endsWith("}")) {
			return getAllProp(key.substring(2, key.length() - 1));
		} else if (key.startsWith("#{") && key.endsWith("}")) {
			return ElEngine.excute(key.substring(2, key.length() - 1)).toString();
		} else {
			return getAllProp(key);
		}
	}

	@Override
	public String getSysEnv(String key) {
		String val = System.getenv(key);
		if (JUtil.isEmpty(val))
			val = System.getProperty(key);
		return val;
	}

	@Override
	public String getProp(String key, String def) {
		return JUtil.notNullDef(getProp(key), def);
	}

	@Override
	public String getSysEnv(String key, String def) {
		return JUtil.notNullDef(getSysEnv(key), def);
	}

	@Override
	public String getProp(FRAME key) {
		return getProp(key.name());
	}

	@Override
	public String getProp(FRAME key, String def) {
		return JUtil.notNullDef(getProp(key), def);
	}

	@Override
	public boolean addFrameProp(FRAME key, String value) {
		String runtime = "FRAME.RUNTIME";
		Properties prop = JUtil.notNullDef(props.get(runtime), new Properties());
		prop.put(key.name(), value);
		props.put(runtime, prop);
		// log.info("[PROP]:{}={}", key, value);
		return true;
	}

	@Override
	public boolean addUserProp(String key, String value) {
		String runtime = "USER.RUNTIME";
		Properties prop = JUtil.notNullDef(props.get(runtime), new Properties());
		prop.put(key, value);
		props.put(runtime, prop);
		log.info("[PROP]:{}={}", key, value);
		return true;
	}

	@Override
	public Properties getProp() {
		Properties prop = new Properties();
		for (Entry<String, Properties> en : props.entrySet())
			prop.putAll(en.getValue());
		return prop;
	}

}
